// Copyright 2006 Bret Taylor
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.

#ifndef INDIE_TYPE_H__
#define INDIE_TYPE_H__

#include <memory>
#include <string>
#include <vector>

#include "ast.h"

using std::auto_ptr;
using std::string;
using std::vector;

namespace indie {

class ArrayType;
class ClassType;
class UnionType;

// Our base Type class, which represents either a class or an array. Supports
// basic comparisons, like subtype and equals.
class Type {
  public:
    virtual ~Type();

    inline const string& name() const {
	return name_;
    }

    bool Equals(const Type& other) const;

    virtual bool IsSubTypeOf(const Type& other) const;
    virtual bool IsSubTypeOf(const ClassType& other) const = 0;
    virtual bool IsSubTypeOf(const ArrayType& other) const = 0;

  protected:
    Type(const string& name);

  private:
    string name_;
};


class Method {
  public:
    Method(const UnionType& return_type);
    virtual ~Method();

    inline const UnionType& return_type() const {
	return return_type_;
    }

    inline const vector<const UnionType*>& parameters() const {
	return parameters_;
    }

    inline void add_parameter(const UnionType& type) {
	parameters_.push_back(&type);
    }

  private:
    const UnionType& return_type_;
    vector<const UnionType*> parameters_;
};


class ClassType : public Type {
  public:
    ClassType(const AstClass& ast, const ClassType* superclass = NULL);
    virtual ~ClassType();

    inline bool has_ast() const {
	return ast_ != NULL;
    }

    inline const AstClass& ast() const {
	assert(has_ast());
	return *ast_;
    }

    inline bool has_superclass() const {
	return superclass_ != NULL;
    }

    inline const ClassType& superclass() const {
	assert(has_superclass());
	return *superclass_;
    }

    inline void set_superclass(const ClassType& superclass) {
	superclass_ = &superclass;
    }

    inline const vector<Method*>& methods() const {
	return methods_;
    }

    inline void add_method(Method* method) {
	methods_.push_back(method);
    }

    virtual bool IsSubTypeOf(const ClassType& other) const;
    virtual bool IsSubTypeOf(const ArrayType& other) const;

    static const ClassType& Object();
    static const ClassType& Int();
    static const ClassType& Float();
    static const ClassType& Char();
    static const ClassType& Boolean();
    static const ClassType& String();

  private:
    ClassType(const string& name, const ClassType* superclass = NULL);

    const AstClass* ast_;
    const ClassType* superclass_;
    vector<Method*> methods_;
};


class ArrayType : public Type {
  public:
    ArrayType(const Type& element_type);
    virtual ~ArrayType();

    inline const Type& element_type() const {
	return element_type_;
    }

    virtual bool IsSubTypeOf(const ClassType& other) const;
    virtual bool IsSubTypeOf(const ArrayType& other) const;

  private:
    const Type& element_type_;
};


class UnionType {
  public:
    UnionType(vector<const Type*>* types);
    virtual ~UnionType();

    inline const vector<const Type*>& types() const {
	return *types_;
    }

    virtual bool Equals(const UnionType& other) const;

    virtual string ToString() const;

    virtual bool IsSubTypeOf(const UnionType& other) const;
    virtual bool IsSubTypeOf(const Type& other) const;

    static const UnionType& Error();
    static const UnionType& Empty();

    // Union type versions of some built-in types
    static const UnionType& Int();
    static const UnionType& Float();
    static const UnionType& Char();
    static const UnionType& Boolean();
    static const UnionType& String();

  private:
    const auto_ptr<vector<const Type*> > types_;
};

}

#endif // INDIE_TYPE_H__
